package cn.com.zero.manager.gateway.filter;

import cn.com.zero.manager.gateway.config.CustomProperty;
import cn.com.zero.manager.gateway.strategy.GrayLoadBalancer;
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;

/**
 * @author Yang Yao
 * @version 1.0.0
 * @Description
 * @createTime 2023/3/2 16:41
 */
@Component
public class GrayReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(GrayReactiveLoadBalancerClientFilter.class);

    /**
     * LoadBalancerClientFilter 执行顺序是 10100
     * 自定义的GrayReactiveLoadBalancerClientFilter的执行顺序>10100
     */
    private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;
    /**
     * LoadBalancerClientFactory的创建基于LoadBalancerClientFilter
     */
    private final LoadBalancerClientFactory clientFactory;
    /**
     * 用户灰度池子
     */
    private final CustomProperty customProperty;

    public GrayReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, CustomProperty customProperty) {
        this.clientFactory = clientFactory;
        this.customProperty = customProperty;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        URI url = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        if(StrUtil.contains(url.getPath(),"/admin/login")){
           return chain.filter(exchange);
        }
        ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);
        return doFilter(exchange, chain, url);
    }

    private Mono<Void> doFilter(ServerWebExchange exchange, GatewayFilterChain chain, URI url) {
        return this.choose(exchange).doOnNext(res -> {
            if (!res.hasServer()) {
                throw NotFoundException.create(true, "Unable to find instance for ".concat(url.getHost()));
            }
            URI uri = exchange.getRequest().getURI();
            DelegatingServiceInstance delegatingServiceInstance = new DelegatingServiceInstance(res.getServer(), null);
            URI reqUrl = this.reconstructURI(delegatingServiceInstance, uri);
            if (log.isDebugEnabled()) {
                log.debug("GrayLoadBalancerClientFilter url chosen: {}", reqUrl.toString());
            }
            exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, reqUrl);
        }).then(chain.filter(exchange));
    }

    protected URI reconstructURI(DelegatingServiceInstance delegatingServiceInstance, URI originalUrl) {
        return LoadBalancerUriTools.reconstructURI(delegatingServiceInstance, originalUrl);
    }

    private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
        URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);

        GrayLoadBalancer loadBalancer = new GrayLoadBalancer(clientFactory.getLazyProvider(uri.getHost(), ServiceInstanceListSupplier.class), uri.getHost(), customProperty);
        if (loadBalancer == null) {
            throw new NotFoundException("No loadbalancer available for " + uri.getHost());
        } else {
            return loadBalancer.choose(this.createRequest(exchange));
        }
    }

    private Request createRequest(ServerWebExchange exchange) {
        return new DefaultRequest(exchange.getRequest().getHeaders());
    }

    @Override
    public int getOrder() {
        return LOAD_BALANCER_CLIENT_FILTER_ORDER;
    }
}
